home *** CD-ROM | disk | FTP | other *** search
/ 17 Bit Software 5: The Fifth Dimension / 17 Bit - The Fifth Dimension (1995)(17 Bit Software)[!].iso / files / 3728.dms / 3728.adf / XPKDisk / devio.c < prev    next >
C/C++ Source or Header  |  1995-04-08  |  28KB  |  1,138 lines

  1. /*-
  2.  * DEVIO.C
  3.  *
  4.  * The xpkdisk.device code that does the real work.
  5.  *
  6.  * $Id: devio.c,v 1.5 1995/04/08 20:21:52 Rhialto Exp $
  7.  * $Log: devio.c,v $
  8.  * Revision 1.5  1995/04/08  20:21:52  Rhialto
  9.  * Add/correct version strings.
  10.  *
  11.  * Revision 1.4  1995/04/02  14:58:51  Rhialto
  12.  * Work around horrible bug in XPK when compressing non-compressible tracks.
  13.  * Allow users to keep old copy of track file when writing the new one fails.
  14.  * Provide more realistic values for the disk geometry with TD_GETGEOMETRY.
  15.  * Re-initialise more stuff (XPKD:, directories, track cache) on CMD_RESET.
  16.  *
  17.  * Revision 1.3  1993/11/08  13:11:15  Rhialto
  18.  * Add RCS tags.
  19.  *
  20.  * This code is (C) Copyright 1989-1995 by Olaf Seibert. All rights reserved.
  21.  * May not be used or copied without a licence.
  22. -*/
  23.  
  24. /*
  25.  * There is a horrible bug in the xpkmaster.library version 2.4.
  26.  * This is the one in Xpk25Usr.lha.
  27.  * See below fow the horrid details.
  28.  */
  29. #define XPKBUG        1
  30.  
  31. #include <string.h>
  32. #include <stdio.h>
  33. #include "xpkdisk.h"
  34. #if NOXPK
  35. #define XPK_MARGIN      0    /* Safety margin for output buffer    */
  36. #define XPKERR_OK      0
  37. #define XPKERR_IOERROUT    -4    /* Output error happened,look at Result2*/
  38. #else
  39. #define LATTICE            /* Get the #pragmas as well */
  40. #include <libraries/xpk.h>
  41. #undef LATTICE
  42. #endif
  43.  
  44. /*#undef DEBUG            */
  45. #if DEBUG
  46. #   include "syslog.h"
  47. #else
  48. #   define    debug(x)
  49. #endif
  50.  
  51. static const char rcsId[] = "$Id: devio.c,v 1.5 1995/04/08 20:21:52 Rhialto Exp $";
  52.  
  53. Prototype void UnitSeglist(void);
  54.  
  55. Prototype void CMD_Read(struct IOStdReq *ioreq, UNIT *unit);
  56. Prototype void CMD_Write(struct IOStdReq *ioreq, UNIT *unit);
  57. Prototype void TD_Format(struct IOStdReq *ioreq, UNIT *unit);
  58. Prototype void CMD_Reset(struct IOStdReq *ioreq, UNIT *unit);
  59. Prototype void CMD_Update(struct IOStdReq *ioreq, UNIT *unit);
  60. Prototype void CMD_Clear(struct IOStdReq *ioreq, UNIT *unit);
  61. Prototype void TD_Seek(struct IOStdReq *ioreq, UNIT *unit);
  62. Prototype void TD_Changenum(struct IOStdReq *ioreq, UNIT *unit);
  63. Prototype void TD_Addchangeint(struct IOStdReq *ioreq, UNIT *unit);
  64. Prototype void TD_Remchangeint(struct IOStdReq *ioreq, UNIT *unit);
  65. Prototype void TD_Getgeometry(struct IOStdReq *ioreq, UNIT *unit);
  66. Prototype void TD_Motor(struct IOStdReq *ioreq, UNIT *unit);
  67. Prototype void TD_Remove(struct IOStdReq *ioreq, UNIT *unit);
  68. Prototype void TD_Protstatus(struct IOStdReq *ioreq, UNIT *unit);
  69. Prototype void TD_Changestate(struct IOStdReq *ioreq, UNIT *unit);
  70. Prototype void TD_Rawread(struct IOStdReq *ioreq, UNIT *unit);
  71. Prototype void TD_Rawwrite(struct IOStdReq *ioreq, UNIT *unit);
  72. Prototype void TD_Getdrivetype(struct IOStdReq *ioreq, UNIT *unit);
  73. Prototype void TD_Getnumtracks(struct IOStdReq *ioreq, UNIT *unit);
  74. Prototype void TD_Eject(struct IOStdReq *ioreq, UNIT *unit);
  75. Prototype void TD_(struct IOStdReq *ioreq, UNIT *unit);
  76.  
  77. Prototype int DevInit(DEV *dev);
  78. Prototype int DevCloseDown(DEV *dev);
  79. Prototype UNIT *UnitInit(DEV *dev, ulong UnitNr);
  80. Prototype int UnitCloseDown(struct IOStdReq *ioreq, DEV *dev, UNIT *unit);
  81.  
  82. struct DosLibrary   *DOSBase;
  83. struct Library        *XpkBase;
  84. struct IntuitionBase *IntuitionBase;
  85.  
  86. /* ------------------------------------------------------------------------- */
  87.  
  88. void           *
  89. GetHead(struct MinList *list)
  90. {
  91.     if ((void *) list->mlh_Head != (void *) &list->mlh_Tail)
  92.     return list->mlh_Head;
  93.     return NULL;
  94. }
  95.  
  96. void           *
  97. GetTail(struct MinList *list)
  98. {
  99.     if ((void *) list->mlh_Head != (void *) &list->mlh_Tail)
  100.     return list->mlh_TailPred;
  101.     return NULL;
  102. }
  103.  
  104. Prototype long min(long a, long b);
  105. long
  106. min(long a, long b)
  107. {
  108.     return a < b? a: b;
  109. }
  110.  
  111. /* ------------------------------------------------------------------------- */
  112.  
  113. struct CacheTrack {
  114.     struct MinNode  trk_Node;
  115.     short        trk_Number;
  116.     ushort        trk_Refcount;
  117.     int         trk_Size;
  118.     byte        trk_Data[0];
  119. };
  120.  
  121. #define TRK_DIRTY   0x8000    /* Bit in trk_Refcount */
  122.  
  123. static const char Directory[] = XPKDISKDIR "Unit%x";
  124.  
  125. int
  126. ReadOnly(UNIT *unit)
  127. {
  128.     __aligned struct InfoData infodata;
  129.     BPTR        fl;
  130.  
  131.     if (fl = Lock(XPKDISKDIR, SHARED_LOCK)) {
  132.     if (Info(fl, &infodata)) {
  133.         unit->xu_ReadOnly = (infodata.id_DiskState != ID_VALIDATED);
  134.     }
  135.     UnLock(fl);
  136.     }
  137.     return unit->xu_ReadOnly;
  138. }
  139.  
  140. Prototype int MakeDirectory(UNIT *unit);
  141.  
  142. int
  143. MakeDirectory(UNIT *unit)
  144. {
  145.     char        dirname[TRACKNAME_LENGTH];
  146.     BPTR        fl;
  147.  
  148.     sprintf(dirname, Directory, unit->xu_UnitNr);
  149.     fl = Lock(dirname, SHARED_LOCK);
  150.     if (fl == 0) {
  151.     if (fl = CreateDir(dirname)) {
  152.         UnLock(fl);     /* exclusive locks not invited */
  153.         fl = Lock(dirname, SHARED_LOCK);
  154.     }
  155.     }
  156.     debug(("New currentdir: %x\n", fl));
  157.     if (fl)
  158.     fl = CurrentDir(fl);
  159.     debug(("Old currentdir: %x\n", fl));
  160.     if (fl)
  161.     UnLock(fl);
  162.     if (!ReadOnly(unit))
  163.     MakeSubDirs(0, unit->xu_NumTracks - 1);
  164.     return (int)fl;
  165. }
  166.  
  167. /* ------------------------------------------------------------------------- */
  168.  
  169. long
  170. ReadTrack(UNIT *unit, struct CacheTrack *trk)
  171. {
  172.     char        filename[TRACKNAME_LENGTH];
  173.     long        res = -1;
  174. #if NOXPK
  175.     BPTR        fh;
  176. #elif DEBUG
  177.     ULONG        OutLen;
  178. #endif
  179.  
  180. #if 0
  181.     if (trk->trk_Number >= unit->xu_NumTracks) {
  182.     unit->xu_NumTracks = trk->trk_Number + 1;
  183.     MakeSubDirs(trk->trk_Number, trk->trk_Number);
  184.     }
  185. #endif
  186.     NewName(filename, trk->trk_Number);
  187.     debug(("ReadTrack: Open %s\n", filename));
  188. #if NOXPK
  189.     if (fh = Open(filename, MODE_OLDFILE)) {
  190.     debug(("ReadTrack: Open %s succeeded\n", filename));
  191.     res = Read(fh, trk->trk_Data, unit->xu_TrackLen);
  192.     if (res >= 0 && res < unit->xu_TrackLen) {
  193.         memset(trk->trk_Data + res, 0, unit->xu_TrackLen - res);
  194.     }
  195.     Close(fh);
  196. #else
  197.     debug(("Call XpkUnpackTags\n"));
  198.     res = XpkUnpackTags(
  199.         XPK_OutBuf, trk->trk_Data,
  200.         XPK_OutBufLen, unit->xu_TrackLen + XPK_MARGIN,
  201. #if DEBUG
  202.         XPK_GetOutLen, &OutLen,
  203. #endif
  204.         XPK_InName, filename,
  205.         XPK_PassThru, 1L,
  206.         TAG_DONE);
  207. #if DEBUG
  208.     if (OutLen != unit->xu_TrackLen) {
  209.     debug(("XpkUnpackTags: wrong unpacked length: %d\n", OutLen));
  210.     }
  211. #endif
  212.     if (res == XPKERR_OK) {
  213. #endif
  214.     } else {
  215.     debug(("ReadTrack: Open %s failed\n", filename));
  216.     memset(trk->trk_Data, 0, unit->xu_TrackLen);
  217.     }
  218.     debug(("res = %d\n", res));
  219.     return res;
  220. }
  221.  
  222. long
  223. WriteFile(char *filename, char *buf, long length, long *ioerr)
  224. {
  225.     BPTR        fh;
  226.     LONG        result;
  227.  
  228.     if (fh = Open(filename, MODE_NEWFILE)) {
  229.     debug(("WriteFile: Open %s succeded\n", filename));
  230.     result = Write(fh, buf, length);
  231.     if (result != length) {
  232.         result = XPKERR_IOERROUT;
  233.         if (ioerr)
  234.         *ioerr = IoErr();
  235.     } else {
  236.         result = XPKERR_OK;
  237.     }
  238.     Close(fh);
  239.     }
  240.  
  241.     return result;
  242. }
  243.  
  244. long
  245. WriteTrack(UNIT *unit, struct CacheTrack *trk)
  246. {
  247.     char        filename[TRACKNAME_LENGTH];
  248.     char        backupname[TRACKNAME_LENGTH + 4];
  249.     char        errmsg[XPKERRMSGSIZE];
  250.     char       *errp = errmsg;
  251. #define RETRY    1
  252. #define REVERT    2
  253. #define ABORT    0
  254.     int         choice = ABORT;
  255.     long        res = -1;
  256.     int         ioerr = 0;
  257.  
  258.     if (trk->trk_Number >= unit->xu_NumTracks) {
  259.     unit->xu_NumTracks = trk->trk_Number + 1;
  260.     MakeSubDirs(trk->trk_Number, trk->trk_Number);
  261.     }
  262.  
  263.     if ((unit->xu_CacheFlags & CACHEF_SAFEWRITE) && !CheckRipcord(unit)) {
  264.     debug(("Set WriteErr\n"));
  265.     unit->xu_WriteErr = TDERR_TooFewSecs;
  266.     return -1;
  267.     }
  268.  
  269.     NewName(filename, trk->trk_Number);
  270.     debug(("WriteTrack: %s\n", filename));
  271.  
  272.     strcpy(backupname, filename);
  273.     strcat(backupname, ".old");
  274.     Rename(filename, backupname);
  275.  
  276.     /* Keep trying to write the track until we succeed. */
  277.     do {
  278. #if !NOXPK
  279. #if !XPKBUG
  280.     debug(("Call XpkPackTags %s\n", unit->xu_XPKPackMethod));
  281.     res = XpkPackTags(
  282.         XPK_InBuf, trk->trk_Data,
  283.         XPK_InLen, (long) unit->xu_TrackLen,
  284.         XPK_OutName, filename,
  285.         XPK_PackMethod, unit->xu_XPKPackMethod,
  286.         XPK_StepDown, 1L,
  287.         XPK_GetError, &errmsg[0],
  288.         TAG_DONE);
  289. #else
  290.     struct XpkFH *xfh;
  291.     long TrackLen = unit->xu_TrackLen;
  292.     char *Data = trk->trk_Data;
  293.     long now;
  294.     long written;
  295.     long save;        /* This is the FIX for the UGLY BUG !! */
  296.     /*
  297.      * Apparently, the master library trashes the longword following
  298.      * the input data, if that data is not compressible.
  299.      * This is also true when using the NONE compression.
  300.      * We can save and restore this longword, because our track
  301.      * buffers have extra space at the end for decompressing.
  302.      */
  303.  
  304.     debug(("Call XpkOpenTags %s\n", unit->xu_XPKPackMethod));
  305.  
  306.     res = XpkOpenTags(&xfh,
  307.         XPK_OutName, filename,
  308.         XPK_PackMethod, unit->xu_XPKPackMethod,
  309.         XPK_StepDown, 1L,
  310.         XPK_GetError, &errmsg[0],
  311.         XPK_InLen, TrackLen,
  312.         XPK_ChunkSize, TrackLen,
  313.         TAG_DONE);
  314.  
  315.     if (res == XPKERR_OK) {
  316.         for (written = 0; written < TrackLen; written += now, Data += now) {
  317.         now = min(xfh->NLen, TrackLen - written);
  318.         if (now <= 0)
  319.             break;
  320.  
  321.         debug(("Call XpkWrite %d\n", now));
  322.         save = *(long *)(&Data[now]);
  323.         res = XpkWrite(xfh, Data, now);
  324.         *(long *)(&Data[now]) = save;
  325.  
  326.         if (res != XPKERR_OK)
  327.             break;
  328.         }
  329.  
  330.         res = XpkClose(xfh);
  331.     }
  332. #endif
  333.     if (res == XPKERR_OK)
  334.         trk->trk_Refcount &= ~TRK_DIRTY;
  335.     else if ((ioerr = IoErr()), res != XPKERR_IOERROUT)
  336. #endif
  337.     {
  338.     write:
  339.         res = WriteFile(filename, trk->trk_Data, unit->xu_TrackLen, &ioerr);
  340.         if (res == XPKERR_OK) {
  341.         trk->trk_Refcount &= ~TRK_DIRTY;
  342.         } else {
  343.         errp = "";
  344.         }
  345.     }
  346.     } while (res != XPKERR_OK &&
  347.          (choice = FullRetry(unit, filename, res, ioerr, errp)) == RETRY);
  348.  
  349.     if (res == XPKERR_OK) {
  350.     DeleteFile(backupname);
  351.     } else if (choice == REVERT) {
  352.     DeleteFile(filename);
  353.     Rename(backupname, filename);
  354.     }
  355. #if 0
  356.     else if (choice == ABORT && !(trk->trk_Refcount & TRK_DIRTY)) {
  357.     DeleteFile(backupname);
  358.     }
  359. #endif
  360.     debug(("res = %d\n", res));
  361.     return res;
  362. }
  363.  
  364.  
  365. /*
  366.  * Find a specific track. The cache list is a Least Recently Used stack:
  367.  * Put it on the head of the cache list. So if it is not used anymore in a
  368.  * long time, it bubbles to the end of the list, getting a higher chance
  369.  * of being trashed for re-use.
  370.  */
  371.  
  372. struct CacheTrack *
  373. FindTrackByNumber(UNIT *unit, int number)
  374. {
  375.     struct CacheTrack *trk;
  376.     struct MinNode  *nexttrk;
  377.  
  378.     debug(("FindTrackByNumber %ld\n", (long)number));
  379.  
  380.     trk = (struct CacheTrack *)unit->xu_Cache.LRUList.mlh_Head;
  381.     while (nexttrk = trk->trk_Node.mln_Succ) {
  382.     if (trk->trk_Number == number) {
  383.         debug((" (%lx) %lx\n", (long)trk->trk_Refcount, trk));
  384.         Remove((struct Node *)&trk->trk_Node);
  385.         AddHead((struct List *)&unit->xu_Cache.LRUList,
  386.             (struct Node *)&trk->trk_Node);
  387.         return trk;
  388.     }
  389.     debug(("cache %ld %lx; ", (long)trk->trk_Number, trk));
  390.     trk = (struct CacheTrack *)nexttrk;
  391.     }
  392.  
  393.     return NULL;
  394. }
  395.  
  396. /*
  397.  * Get a fresh cache buffer. If we are allowed more cache, we just
  398.  * allocate memory. Otherwise, we try to find a currently unused buffer.
  399.  * We start looking at the end of the list, which is the bottom of the LRU
  400.  * stack. If that fails, allocate more memory anyway. Not that is likely
  401.  * anyway, since we currently lock only one track at a time.
  402.  */
  403.  
  404. struct CacheTrack *
  405. NewCacheTrack(UNIT *unit)
  406. {
  407.     struct CacheTrack *trk;
  408.     struct MinNode  *nexttrk;
  409.  
  410.     debug(("NewCacheTrack\n"));
  411.  
  412. #define SIZE (sizeof(*trk) + unit->xu_TrackLen + XPK_MARGIN)
  413.  
  414.     if (unit->xu_CurrentCache < unit->xu_MaxCache) {
  415.     if (trk = AllocMem(SIZE, MEMF_ANY)) {
  416.         goto add;
  417.     }
  418.     }
  419.     for (trk = (struct CacheTrack *)unit->xu_Cache.LRUList.mlh_TailPred;
  420.      nexttrk = trk->trk_Node.mln_Pred;
  421.      trk = (struct CacheTrack *)nexttrk) {
  422.     if ((unit->xu_CurrentCache >= unit->xu_MaxCache) &&
  423.         (trk->trk_Refcount == TRK_DIRTY)) {
  424.         debug(("NewCachetrack: dump dirty trk %d\n", trk->trk_Number));
  425.         FreeCacheTrack(unit, trk);         /* Also writes it to disk */
  426.         continue;
  427.     }
  428.     if (trk->trk_Refcount == 0) {    /* Implies not TRK_DIRTY */
  429.         debug(("NewCachetrk: re-use clean trk %d\n", trk->trk_Number));
  430.         Remove((struct Node *)&trk->trk_Node);
  431.         goto move;
  432.     }
  433.     }
  434.  
  435.     trk = AllocMem(SIZE, MEMF_ANY);
  436.  
  437.     if (trk) {
  438. add:
  439.     trk->trk_Size = SIZE;
  440.     unit->xu_CurrentCache++;
  441. move:
  442.     AddHead((struct List *) &unit->xu_Cache.LRUList,
  443.         (struct Node *) &trk->trk_Node);
  444.     }
  445.  
  446.     debug(("NewCacheTrack: %lx\n", trk));
  447.     return trk;
  448. #undef SIZE
  449. }
  450.  
  451. /*
  452.  * Dispose a cached track, even if it has a non-zero refcount. If it is
  453.  * dirty, write it out.
  454.  * If an error occurs, stop.
  455.  */
  456. Prototype int FreeCacheTrack(UNIT *unit, struct CacheTrack *trk);
  457. int
  458. FreeCacheTrack(UNIT *unit, struct CacheTrack *trk)
  459. {
  460.     int         error;
  461.  
  462.     debug(("FreeCachetrk %ld\n", (long)trk->trk_Number));
  463.  
  464.     if (trk->trk_Refcount & ~TRK_DIRTY) {
  465.     debug(("\n\t*** PANIC!!! Refcount not 0 (%x) !!! ***\n\n", trk->trk_Refcount));
  466.     trk->trk_Refcount &= TRK_DIRTY;
  467.     }
  468.  
  469.     if (trk->trk_Refcount & TRK_DIRTY) {
  470.     error = WriteTrack(unit, trk);
  471.     } else
  472.     error = 0;
  473.  
  474.     if (error == 0) {
  475.     Remove((struct Node *)&trk->trk_Node);
  476.     FreeMem(trk, trk->trk_Size);
  477.     unit->xu_CurrentCache--;
  478.     }
  479.  
  480.     return error;
  481. }
  482.  
  483. /*
  484.  * Create an empty cache
  485.  */
  486.  
  487. void
  488. InitCache(UNIT *unit)
  489. {
  490.     NewList((struct List *)&unit->xu_Cache.LRUList);
  491.     unit->xu_MaxCache = MAX_CACHE;
  492.     unit->xu_CurrentCache = 0;
  493.     unit->xu_CacheDirty = 0;
  494.     unit->xu_CacheFlags = CACHE_FLAGS;
  495.     unit->xu_CacheTimeout = CACHE_TIMEOUT;
  496. }
  497.  
  498. /*
  499.  * Dispose all cached tracks, possibly writing them to disk.
  500.  * If an error occurs, stop.
  501.  */
  502.  
  503. int
  504. FreeCacheList(UNIT *unit)
  505. {
  506.     struct CacheTrack *trk;
  507.     int         error;
  508.  
  509.     debug(("FreeCacheList, %ld\n", (long)unit->xu_CurrentCache));
  510.     while ((trk = GetHead(&unit->xu_Cache.LRUList)) &&
  511.        (error = FreeCacheTrack(unit, trk)) == 0) {
  512.    }
  513.  
  514.     if (error == 0) {
  515.     debug(("Clear WriteErr\n"));
  516.     unit->xu_WriteErr = 0;
  517.     }
  518.     return error;
  519. }
  520.  
  521. struct CacheTrack *
  522. GetTrack(struct IOStdReq *ioreq, int track)
  523. {
  524.     UNIT       *unit;
  525.     struct CacheTrack *trk;
  526.  
  527.     debug(("GetTrack %ld\n", (long)track));
  528.     unit = (UNIT *) ioreq->io_Unit;
  529.  
  530.     if (trk = FindTrackByNumber(unit, track)) {
  531.     trk->trk_Refcount++;
  532.     return trk;
  533.     }
  534.  
  535.     if (trk = NewCacheTrack(unit)) {
  536.     trk->trk_Refcount = 1;
  537.     trk->trk_Number = track;
  538.     ReadTrack(unit, trk);
  539.     return trk;
  540.     }
  541.  
  542.     return NULL;
  543. }
  544.  
  545. void
  546. MarkTrackDirty(UNIT *unit, struct CacheTrack *trk)
  547. {
  548.     if (trk) {
  549.     trk->trk_Refcount |= TRK_DIRTY;
  550.     unit->xu_CacheDirty = 1;
  551.     }
  552. }
  553.  
  554. /*
  555.  * Unlock a cached track. When the usage count drops to zero, which
  556.  * implies it is not dirty, and we are over our cache quota, the sector is
  557.  * freed. Otherwise we keep it for re-use.
  558.  */
  559.  
  560. void
  561. FreeTrack(UNIT *unit, struct CacheTrack *trk)
  562. {
  563.     if (trk) {
  564.     --trk->trk_Refcount;
  565.     /*
  566.      * If we need to dump cache then dump some long-unused track.
  567.      */
  568.     while (unit->xu_CurrentCache > unit->xu_MaxCache &&
  569.         (trk = GetTail(&unit->xu_Cache.LRUList)) &&
  570.         (trk->trk_Refcount & ~TRK_DIRTY) == 0) {
  571.         debug(("Freetrk: dump %s trk %d\n",
  572.             (trk->trk_Refcount & TRK_DIRTY)? "dirty" : "clean",
  573.             trk->trk_Number));
  574.         FreeCacheTrack(unit, trk);
  575.     }
  576.     }
  577. }
  578.  
  579. /*
  580.  * Decide if we must do an update.
  581.  */
  582.  
  583. int
  584. Internal_Update(UNIT *unit)
  585. {
  586.     struct CacheTrack *trk;
  587.     struct MinNode *nexttrk;
  588.     int         error = 0;
  589.  
  590.     debug(("Internal_Update\n"));
  591.     /* Is the cache dirty? */
  592.     if (unit->xu_CacheDirty == 0) {
  593.     debug(("Cache not dirty\n"));
  594.     goto cleartriggers;
  595.     }
  596.  
  597.     /* Did we get a CMD_UPDATE, if required? */
  598.     if ((unit->xu_CacheFlags & (CACHEF_CMDUPDATE|CACHEF_GOTCMDUPD)) == CACHEF_CMDUPDATE) {
  599.     debug(("No required UPDATE\n"));
  600.     return 0;
  601.     }
  602.  
  603.     /* Did we get a timeout, if required? */
  604.     if ((unit->xu_CacheFlags & (CACHEF_DELAY|CACHEF_GOTTIMEOUT)) == CACHEF_DELAY) {
  605.     debug(("No required TIMEOUT\n"));
  606.     return 0;
  607.     }
  608.  
  609.     for (trk = (struct CacheTrack *)unit->xu_Cache.LRUList.mlh_TailPred;
  610.      nexttrk = trk->trk_Node.mln_Pred;
  611.      trk = (struct CacheTrack *)nexttrk) {
  612.     if (trk->trk_Refcount & TRK_DIRTY) {
  613.         if (error = WriteTrack(unit, trk)) {
  614.         goto end;    /* Don't clear update conditions */
  615.         }
  616.     }
  617.     }
  618.  
  619.     unit->xu_CacheDirty = 0;
  620.     if (error == 0) {
  621.     debug(("Clear WriteErr\n"));
  622.     unit->xu_WriteErr = 0;
  623.     }
  624. cleartriggers:
  625.     unit->xu_CacheFlags &= ~(CACHEF_GOTCMDUPD | CACHEF_GOTTIMEOUT);
  626. end:
  627.     return error;
  628. }
  629.  
  630. /* ------------------------------------------------------------------------- */
  631.  
  632. /*
  633.  * Read zero or more sectors from the disk and copy them into the user's
  634.  * buffer.
  635.  */
  636.  
  637. void
  638. CMD_Read(struct IOStdReq *ioreq, UNIT *unit)
  639. {
  640.     int         track;
  641.     byte       *userbuf;
  642.     long        length;
  643.     long        offset;
  644.     struct CacheTrack *trk;
  645.     int         error;
  646.  
  647.     debug(("CMD_Read "));
  648.     userbuf = (byte *) ioreq->io_Data;
  649.     length = ioreq->io_Length;
  650.     offset = ioreq->io_Offset;
  651.     debug(("userbuf %08lx off %ld len %ld\n", userbuf, offset, length));
  652.  
  653.     track = offset / unit->xu_TrackLen;
  654.     offset = offset % unit->xu_TrackLen;
  655.     debug(("Tr=%ld Offset=%ld\n", (long)track, (long)offset));
  656.  
  657.     ioreq->io_Actual = length;
  658.     error = TDERR_NoError;
  659.  
  660.     if (length <= 0)
  661.     goto end;
  662.  
  663.     if (offset != 0) {
  664.     /* Handle non-track  part first */
  665.     ulong        l;
  666.  
  667.     trk = GetTrack(ioreq, track);
  668.     if (trk == NULL) {
  669.         error = TDERR_NoSecHdr;
  670.         goto end;
  671.     }
  672.     l = min(length, unit->xu_TrackLen - offset);
  673.  
  674.     CopyMem(trk->trk_Data + offset, userbuf, l);
  675.     userbuf += l;
  676.     length -= l;
  677.     track++;
  678.     FreeTrack(unit, trk);
  679.     }
  680.  
  681.     while (length > 0) {
  682.     ulong        l;
  683.  
  684.     trk = GetTrack(ioreq, track);
  685.     if (trk == NULL) {
  686.         error = TDERR_NoSecHdr;
  687.         goto end;
  688.     }
  689.     l = min(length, unit->xu_TrackLen);
  690.  
  691.     CopyMem(trk->trk_Data, userbuf, l);
  692.     userbuf += l;
  693.     length -= l;
  694.     track++;
  695.     FreeTrack(unit, trk);
  696.     }
  697.  
  698. end:
  699.     ioreq->io_Error = error;
  700.     if (error != TDERR_NoError)
  701.     ioreq->io_Actual = 0;
  702.     TermIO(ioreq);
  703. }
  704.  
  705. void
  706. CMD_Write(struct IOStdReq *ioreq, UNIT *unit)
  707. {
  708.     int         track;
  709.     byte       *userbuf;
  710.     long        length;
  711.     long        offset;
  712.     struct CacheTrack *trk;
  713.     int         error;
  714.  
  715.     debug(("CMD_Write "));
  716.  
  717.     if (unit->xu_ReadOnly) {
  718.     error = TDERR_WriteProt;
  719.     goto end;
  720.     }
  721.  
  722.     userbuf = (byte *) ioreq->io_Data;
  723.     length = ioreq->io_Length;
  724.     offset = ioreq->io_Offset;
  725.     debug(("userbuf %08lx off %ld len %ld\n", userbuf, offset, length));
  726.  
  727.     track = offset / unit->xu_TrackLen;
  728.     offset = offset % unit->xu_TrackLen;
  729.     debug(("Tr=%ld Offset=%ld\n", (long)track, (long)offset));
  730.  
  731.     ioreq->io_Actual = length;
  732.     error = TDERR_NoError;
  733.  
  734.     if (length <= 0)
  735.     goto end;
  736.  
  737.     if (offset != 0) {
  738.     /* Handle non-track aligned part first */
  739.     ulong        l;
  740.  
  741.     trk = GetTrack(ioreq, track);
  742.     if (trk == NULL) {
  743.         error = TDERR_NoSecHdr;
  744.         goto end;
  745.     }
  746.     l = min(length, unit->xu_TrackLen - offset);
  747.  
  748.     CopyMem(userbuf, trk->trk_Data + offset, l);
  749.     userbuf += l;
  750.     length -= l;
  751.     track++;
  752.     MarkTrackDirty(unit, trk);
  753.     FreeTrack(unit, trk);
  754.     }
  755.  
  756.     while (length > 0) {
  757.     ulong        l;
  758.  
  759.     trk = GetTrack(ioreq, track);
  760.     if (trk == NULL) {
  761.         error = TDERR_NoMem;
  762.         goto end;
  763.     }
  764.     l = min(length, unit->xu_TrackLen);
  765.  
  766.     CopyMem(userbuf, trk->trk_Data, l);
  767.     userbuf += l;
  768.     length -= l;
  769.     track++;
  770.     MarkTrackDirty(unit, trk);
  771.     FreeTrack(unit, trk);
  772.     }
  773.  
  774. end:
  775.     if (error == 0 && unit->xu_WriteErr != 0) {
  776.     /* Propagate earlier write error(s) of previous tracks */
  777.     debug(("Use & Clear WriteErr\n"));
  778.     error = unit->xu_WriteErr;
  779.     unit->xu_WriteErr = 0;
  780.     }
  781.     ioreq->io_Error = error;
  782.     if (error != TDERR_NoError)
  783.     ioreq->io_Actual = 0;
  784.     TermIO(ioreq);
  785.  
  786.     StartTimeout(unit);
  787. }
  788.  
  789. void
  790. TD_Format(struct IOStdReq *ioreq, UNIT *unit)
  791. {
  792.     debug(("CMD_Format "));
  793.     /* keep it simple for now */
  794.     CMD_Write(ioreq, unit);
  795. }
  796.  
  797. void
  798. CMD_Reset(struct IOStdReq *ioreq, UNIT *unit)
  799. {
  800.     debug(("CMD_Reset\n"));
  801. #if 1
  802.     /*
  803.      * Somebody may have changed XPKD: to point somewhere else,
  804.      * so push the cache and then re-establish the current dir.
  805.      */
  806.     FreeCacheList(unit);    /* Just to be sure */
  807.     MakeDirectory(unit);
  808. #endif
  809.     MagicInit(unit);        /* Get mountlist info and user prefs */
  810.     if (!ReadOnly(unit) && (unit->xu_CacheFlags & CACHEF_SAFEWRITE))
  811.     MakeRipcord(unit);
  812.     TermIO(ioreq);
  813. }
  814.  
  815. void
  816. CMD_Update(struct IOStdReq *ioreq, UNIT *unit)
  817. {
  818.     debug(("CMD_Update\n"));
  819.     unit->xu_CacheFlags |= CACHEF_GOTCMDUPD;
  820.     StartTimeout(unit);
  821.     ioreq->io_Error = Internal_Update(unit);
  822.     TermIO(ioreq);
  823. }
  824.  
  825. void
  826. CMD_Clear(struct IOStdReq *ioreq, UNIT *unit)
  827. {
  828.     debug(("CMD_Clear\n"));
  829.     ioreq->io_Error = FreeCacheList(unit);
  830.     TermIO(ioreq);
  831. }
  832.  
  833. void
  834. TD_Motor(struct IOStdReq *ioreq, UNIT *unit)
  835. {
  836.     debug(("TD_Motor\n"));
  837.     ioreq->io_Actual = 1;    /* Motor is running */
  838.     TermIO(ioreq);
  839. }
  840.  
  841. void
  842. TD_Return0(struct IOStdReq *ioreq, UNIT *unit)
  843. {
  844.     debug(("TD_Seek, TD_Changenum, TD_Changestate\n"));
  845.     ioreq->io_Actual = 0;
  846.     TermIO(ioreq);
  847. }
  848.  
  849. void
  850. TD_Protstatus(struct IOStdReq *ioreq, UNIT *unit)
  851. {
  852.     debug(("TD_Protstatus\n"));
  853.     ioreq->io_Actual = ReadOnly(unit);
  854.     TermIO(ioreq);
  855. }
  856.  
  857. void
  858. TD_Getnumtracks(struct IOStdReq *ioreq, UNIT *unit)
  859. {
  860.     debug(("TD_Getnumtracks\n"));
  861.     ioreq->io_Actual = unit->xu_NumTracks;  /* a guess */
  862.     TermIO(ioreq);
  863. }
  864.  
  865. /*
  866.  * Handle disk change interrupts. However, our disks never get taken
  867.  * out of their drives.
  868.  */
  869.  
  870. void
  871. TD_Addchangeint(struct IOStdReq *ioreq, UNIT *unit)
  872. {
  873.     Enqueue((struct List *)&unit->xu_ChangeIntList, &ioreq->io_Message.mn_Node);
  874.     ioreq->io_Flags &= ~IOF_QUICK;    /* So we call ReplyMsg instead of
  875.                      * TermIO */
  876.     /* Note no TermIO */
  877. }
  878.  
  879. void
  880. TD_Remchangeint(struct IOStdReq *ioreq, UNIT *unit)
  881. {
  882.     struct IOStdReq *intreq;
  883.  
  884.     intreq = (struct IOStdReq *) ioreq->io_Data;
  885.     Remove(&intreq->io_Message.mn_Node);
  886.     ReplyMsg(&intreq->io_Message);    /* Quick bit always cleared */
  887.     ioreq->io_Error = 0;
  888.     TermIO(ioreq);
  889. }
  890.  
  891. void
  892. TD_Getgeometry(struct IOStdReq *ioreq, UNIT *unit)
  893. {
  894. #if defined(TD_GETGEOMETRY)
  895.     struct DriveGeometry *dg;
  896.     short numtracks;
  897.  
  898.     debug(("TD_Getgeometry\n"));
  899.     dg = (struct DriveGeometry *)ioreq->io_Data;
  900.  
  901.     numtracks = unit->xu_NumTracks;
  902.     dg->dg_SectorSize = XD_BPS;
  903.  
  904.     dg->dg_Cylinders = numtracks;
  905.     dg->dg_CylSectors = unit->xu_TrackLen / XD_BPS;
  906.  
  907.     dg->dg_TotalSectors = numtracks * dg->dg_CylSectors;
  908.  
  909.     dg->dg_Heads = 1;
  910.     dg->dg_TrackSectors = dg->dg_CylSectors;
  911.  
  912.     dg->dg_BufMemType = MEMF_PUBLIC;
  913.     dg->dg_DeviceType = DG_DIRECT_ACCESS;
  914.     dg->dg_Flags = 0;        /* not DGF_REMOVABLE */
  915. #else
  916.     ioreq->io_Error = IOERR_NOCMD;
  917. #endif
  918.     TermIO(ioreq);
  919. }
  920.  
  921. /* ------------------------------------------------------------------------- */
  922.  
  923. #define ROUNDS        2
  924.  
  925. void
  926. StartTimer(UNIT *unit)
  927. {
  928.     WaitIO(&unit->xu_TimeReq.tr_node);
  929.     unit->xu_TimeReq.tr_node.io_Command = TR_ADDREQUEST;
  930.     unit->xu_TimeReq.tr_time.tv_secs = unit->xu_CacheTimeout / ROUNDS;
  931.     unit->xu_TimeReq.tr_time.tv_micro =
  932.             (unit->xu_CacheTimeout % ROUNDS) * (1000000 / ROUNDS);
  933.     SendIO(&unit->xu_TimeReq.tr_node);
  934. }
  935.  
  936. Prototype void PollTimer(UNIT *unit);
  937. void
  938. PollTimer(UNIT *unit)
  939. {
  940.     if (CheckIO(&unit->xu_TimeReq.tr_node)) {
  941.     if (--unit->xu_TimeoutCounter <= 0) {
  942.         unit->xu_CacheFlags |= CACHEF_GOTTIMEOUT;
  943.         (void)Internal_Update(unit);
  944.     } else {
  945.         StartTimer(unit);
  946.     }
  947.     }
  948. }
  949.  
  950. Prototype void StartTimeout(UNIT *unit);
  951.  
  952. void
  953. StartTimeout(UNIT *unit)
  954. {
  955.     unit->xu_TimeoutCounter = ROUNDS;
  956.     if (CheckIO(&unit->xu_TimeReq.tr_node))
  957.     StartTimer(unit);
  958. }
  959.  
  960. int
  961. DevInit(DEV *dev)
  962. {
  963.     debug(("DevInit: open dos\n"));
  964.     DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 0);
  965.  
  966.     debug(("done DevInit\n"));
  967.     return 1;            /* Initializing succeeded */
  968.  
  969. abort:
  970.     return DevCloseDown(dev);
  971. }
  972.  
  973. int
  974. DevCloseDown(dev)
  975. DEV           *dev;
  976. {
  977.     if (DOSBase) {
  978.     CloseLibrary((struct Library *)DOSBase);
  979.     DOSBase = NULL;
  980.     }
  981.     return 0;            /* Now unitialized */
  982. }
  983.  
  984. ulong
  985. InitMsgPort(struct MsgPort *p)
  986. {
  987.     p->mp_SigTask = FindTask(NULL);
  988.     p->mp_SigBit = AllocSignal(-1);
  989.     p->mp_Flags = PA_SIGNAL;
  990.     Forbid();
  991.     /*
  992.      * We must Forbid() here to prevent a race condition. That is also
  993.      * sufficient, since interrupts are not allowed to call BeginIO().
  994.      */
  995.     if (p->mp_MsgList.lh_Head == NULL)
  996.     NewList(&p->mp_MsgList);
  997.     Permit();
  998.  
  999.     return 1L << p->mp_SigBit;
  1000. }
  1001.  
  1002. UNIT           *
  1003. UnitInit(dev, UnitNr)
  1004. DEV           *dev;
  1005. ulong        UnitNr;
  1006. {
  1007.     UNIT       *unit;
  1008.     struct Task    *task;
  1009.     struct MsgPort *p;
  1010.  
  1011.     unit = AllocMem((long) sizeof (UNIT), MEMF_PUBLIC | MEMF_CLEAR);
  1012.     if (unit == NULL)
  1013.     return NULL;
  1014.  
  1015.     unit->xu_UnitNr = UnitNr;
  1016.     /*
  1017.      * Now create the Unit process. Remember that it won't start running
  1018.      * since we are Forbid()den. But just to be sure, we Forbid() again.
  1019.      */
  1020.     debug(("call CreateProc %x (%x)\n",
  1021.         MKBADDR(UnitSeglist), UnitSeglist));
  1022.     Forbid();
  1023.     p = CreateProc(DevName, TASKPRI, MKBADDR(UnitSeglist), TASKSTACK);
  1024.     if (p == NULL) {
  1025.     Permit();
  1026.     debug(("*** No Unit process!!!\n"));
  1027.     goto abort;
  1028.     }
  1029.     task = (struct Task *)(((char *)p) - offsetof(struct Process, pr_MsgPort));
  1030.     task->tc_UserData = (APTR) unit;
  1031.  
  1032.     unit->xu_Port.mp_Flags = PA_IGNORE;
  1033.     unit->xu_Port.mp_SigTask = task;
  1034.     NewList(&unit->xu_Port.mp_MsgList);
  1035.  
  1036.     Permit();
  1037.     debug(("task: %lx\n", task));
  1038.  
  1039.     return unit;
  1040.  
  1041. abort:
  1042.     UnitCloseDown(NULL, dev, unit);
  1043.     return NULL;
  1044. }
  1045.  
  1046. Prototype ulong UnitInit2(UNIT *unit);
  1047. ulong
  1048. UnitInit2(UNIT *unit)
  1049. {
  1050.     ulong        waitmask;
  1051.  
  1052.     waitmask = InitMsgPort(&unit->xu_Port);
  1053.     waitmask |= InitMsgPort(&unit->xu_TimerPort);
  1054.  
  1055.     unit->xu_TimeReq.tr_node.io_Message.mn_ReplyPort = &unit->xu_TimerPort;
  1056.     if (OpenDevice("timer.device", UNIT_VBLANK, &unit->xu_TimeReq.tr_node, 0))
  1057.     return 0;
  1058.     unit->xu_TimeReq.tr_node.io_Flags = IOF_QUICK;
  1059.  
  1060.     NewList((struct List *)&unit->xu_ChangeIntList);
  1061.     unit->xu_TrackLen = XD_TRACKLEN;
  1062.     strncpy(unit->xu_XPKPackMethod, PACKING_METHOD, 9);
  1063.     InitCache(unit);
  1064.     MagicInit(unit);        /* Get mountlist info and user prefs */
  1065.     MakeDirectory(unit);
  1066.     if (!unit->xu_ReadOnly && (unit->xu_CacheFlags & CACHEF_SAFEWRITE))
  1067.     MakeRipcord(unit);
  1068.  
  1069. #if !NOXPK
  1070.     XpkBase = OpenLibrary("xpkmaster.library", 0);
  1071.     if (XpkBase == NULL)
  1072.     return 0;
  1073. #endif
  1074.     IntuitionBase = OpenLibrary("intuition.library", 33);
  1075.     if (IntuitionBase == NULL)
  1076.     return 0;
  1077.  
  1078.     return waitmask;
  1079. }
  1080.  
  1081. Prototype void UnitCloseDown2(UNIT *unit);
  1082. void
  1083. UnitCloseDown2(UNIT *unit)
  1084. {
  1085.     /*
  1086.      * There may still be dirty tracks due to delayed updates.
  1087.      * If an error occurs while writing the tracks out, we currently
  1088.      * lose the memory.
  1089.      */
  1090.     (void)FreeCacheList(unit);
  1091.     {
  1092.     struct IORequest *ioreq;
  1093.     while (ioreq = (struct IORequest *)
  1094.                RemHead((struct List *)&unit->xu_ChangeIntList)) {
  1095.         ReplyMsg(&ioreq->io_Message);
  1096.     }
  1097.     }
  1098.  
  1099.     UnLock(CurrentDir(0));
  1100.  
  1101.     AbortIO(&unit->xu_TimeReq.tr_node);
  1102.     WaitIO(&unit->xu_TimeReq.tr_node);
  1103.     CloseDevice(&unit->xu_TimeReq.tr_node);
  1104.     if (IntuitionBase)
  1105.     CloseLibrary(IntuitionBase);
  1106.     if (XpkBase)
  1107.     CloseLibrary(XpkBase);
  1108. }
  1109.  
  1110. int
  1111. UnitCloseDown(struct IOStdReq *ioreq, DEV *dev, UNIT *unit)
  1112. {
  1113.     /*
  1114.      * Get rid of the Unit's task. We know this is safe because the unit
  1115.      * has an open count of zero, so it is 'guaranteed' not in use.
  1116.      */
  1117.  
  1118.     if (unit->xu_Port.mp_SigTask) {
  1119.     struct IORequest io;
  1120.  
  1121.     io.io_Message.mn_ReplyPort = (struct MsgPort *)FindTask(NULL);
  1122.     io.io_Command = CMD_Die;
  1123.     unit->xu_Flags &= ~UNITF_STOPPED;
  1124.     debug(("Asking task to die\n"));
  1125.     PutMsg(&unit->xu_Port, &io.io_Message);
  1126.     do {
  1127.         Wait(SIGF_SINGLE);
  1128.         debug(("Someone exhaled...\n"));
  1129.     } while (io.io_Message.mn_Node.ln_Type != NT_REPLYMSG);
  1130.     debug(("That was the last gasp!\n"));
  1131.     }
  1132.     FreeMem(unit, (long) sizeof (UNIT));
  1133.  
  1134.     return 0;            /* Now unitialized */
  1135. }
  1136.  
  1137. /* ------------------------------------------------------------------------- */
  1138.